/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.common.metadata;


import com.inspur.edp.cdp.web.component.metadata.define.WebComponentMetadata;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.entity.MetadataProject;
import com.inspur.edp.lcm.metadata.devcommon.ManagerUtils;
import com.inspur.edp.web.command.component.metadata.*;
import com.inspur.edp.web.common.constant.WebCommonExceptionConstant;
import com.inspur.edp.web.common.customexception.WebCustomException;
import com.inspur.edp.web.common.environment.ExecuteEnvironment;
import com.inspur.edp.web.common.io.FileUtility;
import com.inspur.edp.web.common.utility.StringUtility;

import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;

/**
 * 元数据工具类
 *
 * @author noah
 */
public final class MetadataUtility {

    private MetadataUtility() {

    }

    /**
     * 不再保持单例 使用spring自带的缓存bean进行读取
     *
     * @return
     */
    public static MetadataUtility getInstance() {
        return new MetadataUtility();
    }

    /**
     * 根据元数据名称及元数据所在路径获取元数据信息。该方法不支持跨工程引用
     *
     * @param metadataFileName
     * @param metadataPath
     * @return
     */
    public GspMetadata getMetadataWithDesign(String metadataFileName, String metadataPath) {
        return MetadataUtilityWithDesign.getMetadata(metadataFileName, metadataPath);
    }

    /**
     * @param metadataGetterParameter
     * @return
     */
    public GspMetadata getMetadataWithDesign(MetadataGetterParameter metadataGetterParameter) {
        return MetadataUtilityWithDesign.getMetadata(metadataGetterParameter);
    }

    /**
     * 获取指定类型的元数据
     *
     * @param projectPath
     * @param metadataType
     * @return
     */
    public List<GspMetadata> getMetadataListInProjectWithDesign(String projectPath, String metadataType) {
        return MetadataUtilityWithDesign.getMetadataListInProject(projectPath, metadataType);
    }

    /**
     * 删除对应的元数据物理文件
     *
     * @param metadataPath     元数据所属路径
     * @param metadataFileName 元数据对应的文件名称
     */
    public void deleteMetadataWithDesign(String metadataPath, String metadataFileName) {
        MetadataUtilityWithDesign.deleteMetadata(metadataPath, metadataFileName);
    }

    /**
     * 元数据保存  如果元数据已经存在，则先进行删除旧的元数据文件
     *
     * @param metadata
     */
    public void saveMetadataWithDesign(GspMetadata metadata) {
        MetadataUtilityWithDesign.saveMetadata(metadata);
    }

    public List<GspMetadata> getMetadataListWithDesign(String spacePath, List<String> metadataTypes) {
        return MetadataUtilityWithDesign.getMetadataList(spacePath, metadataTypes);
    }

    public List<GspMetadata> getMetadataListWithDesign(String spacePath) {
        return MetadataUtilityWithDesign.getMetadataList(spacePath, null);
    }

    /**
     * 创建表单元数据  如果已经存在 不进行创建，否则进行元数据的创建
     *
     * @param metadata 待保存元数据
     */
    public void createMetadataIfNotExistsWithDesign(GspMetadata metadata) {
        MetadataUtilityWithDesign.createMetadataIfNotExists(metadata);
    }


    /**
     * 判断指定路径 指定文件名称元数据是否存在
     *
     * @param spacePath
     * @param metadataId
     * @return
     */
    public boolean isMetaDataExistsWithMetadataIDAndPathWithDesign(String spacePath, String metadataId) {
        return MetadataUtilityWithDesign.isMetaDataExistsWithMetadataIDAndPath(spacePath, metadataId);
    }

    /**
     * 判断指定工程路径下指定的元数据文件是否存在
     *
     * @param metaDataPath 对应的元数据所在工程路径
     * @param fileName     对应的元数据文件名称
     * @return
     */
    public boolean isMetaDataExistsWithMetadataPathAndFileNameWithDesign(String metaDataPath, String fileName) {
        return MetadataUtilityWithDesign.isMetaDataExistsWithMetadataPathAndFileName(metaDataPath, fileName);
    }

    /**
     * 根据元数据参数信息获取对应的元数据
     *
     * @param targetMetadataInfo                  目标元数据参数配置
     * @param sourceMetadataInfo                  源 元数据参数配置
     * @param customTargetMetadataNotFoundMessage 自定义 元数据找不到异常提示
     * @param executeEnvironment
     * @param isInUpgradeTool
     * @return
     */
    public GspMetadata getMetadataWithEnvironment(MetadataGetterParameter.GetterMetadataInfo targetMetadataInfo,
                                                  MetadataGetterParameter.GetterMetadataInfo sourceMetadataInfo,
                                                  String customTargetMetadataNotFoundMessage,
                                                  ExecuteEnvironment executeEnvironment, boolean isInUpgradeTool) {
        if (targetMetadataInfo == null || StringUtility.isNullOrEmpty(targetMetadataInfo.getId())) {
            return null;
        }

        // 如果在升级工具中执行
        if (isInUpgradeTool) {
            if (MetadataUtilityWithRuntimeRpc.hasCustomizationServerServiceInstance()) {
                return MetadataUtilityWithRuntimeRpc.getMetadata(targetMetadataInfo.getId());
            } else {
                // 如果获取不到 那么设定执行运行时获取
                executeEnvironment = ExecuteEnvironment.Runtime;
            }
        }

        if (executeEnvironment.equals(ExecuteEnvironment.Design)) {
            // 设计时元数据获取
            MetadataGetterParameter metadataGetterParameter = MetadataGetterParameter.getNewInstance();
            metadataGetterParameter.setTargetMetadataInfo(targetMetadataInfo);
            metadataGetterParameter.setSourceMetadataInfo(sourceMetadataInfo);
            metadataGetterParameter.setTargetMetadataNotFoundMessage(customTargetMetadataNotFoundMessage);
            return MetadataUtilityWithDesign.getMetadata(metadataGetterParameter.getTargetMetadataUri(), metadataGetterParameter.getSourceMetadataUri(), targetMetadataInfo.getPath());
        } else {
            return MetadataUtilityWithRuntime.getMetadata(targetMetadataInfo.getId());
        }
    }

    /**
     * 根据元数据id获取元数据信息
     *
     * @param targetMetadataInfo 目标元数据信息不能为空
     * @param sourceMetadataInfo 如果为运行时，可以为空
     * @param executeEnvironment 执行环境：运行时或设计时
     * @param isInUpgradeTool    是否运行在补丁工具中
     * @return
     */
    public GspMetadata getMetadataWithEnvironment(MetadataGetterParameter.GetterMetadataInfo targetMetadataInfo,
                                                  MetadataGetterParameter.GetterMetadataInfo sourceMetadataInfo,
                                                  ExecuteEnvironment executeEnvironment, boolean isInUpgradeTool) {
        return this.getMetadataWithEnvironment(targetMetadataInfo, sourceMetadataInfo, null, executeEnvironment, isInUpgradeTool);
    }


    public GspMetadata getMetadataWithEnvironment(Supplier<MetadataGetterParameter.GetterMetadataInfo> targetMetadataInfoSupplierAction,
                                                  Supplier<MetadataGetterParameter.GetterMetadataInfo> sourceMetadataInfoSupplierAction,
                                                  ExecuteEnvironment executeEnvironment, boolean isInUpgradeTool) {
        if (targetMetadataInfoSupplierAction == null) {
            return null;
        }
        MetadataGetterParameter.GetterMetadataInfo targetMetadataInfo = targetMetadataInfoSupplierAction.get();
        MetadataGetterParameter.GetterMetadataInfo sourceMetadataInfo = null;
        if (sourceMetadataInfoSupplierAction != null) {
            sourceMetadataInfo = sourceMetadataInfoSupplierAction.get();
        }
        return getMetadataWithEnvironment(targetMetadataInfo, sourceMetadataInfo, executeEnvironment, isInUpgradeTool);
    }

    public GspMetadata getMetadataWithEnvironment(Supplier<MetadataGetterParameter.GetterMetadataInfo> targetMetadataInfoSupplierAction,
                                                  Supplier<MetadataGetterParameter.GetterMetadataInfo> sourceMetadataInfoSupplierAction,
                                                  String customTargetMetadataNotFoundMessage,
                                                  ExecuteEnvironment executeEnvironment, boolean isInUpgradeTool) {
        if (targetMetadataInfoSupplierAction == null) {
            return null;
        }
        MetadataGetterParameter.GetterMetadataInfo targetMetadataInfo = targetMetadataInfoSupplierAction.get();
        MetadataGetterParameter.GetterMetadataInfo sourceMetadataInfo = null;
        if (sourceMetadataInfoSupplierAction != null) {
            sourceMetadataInfo = sourceMetadataInfoSupplierAction.get();
        }
        return getMetadataWithEnvironment(targetMetadataInfo, sourceMetadataInfo, customTargetMetadataNotFoundMessage, executeEnvironment, isInUpgradeTool);
    }

    /**
     * 运行时获取元数据接口方法
     * 作为公共方法  会在别处进行使用 定义后不允许更改
     *
     * @param metadataId
     * @return
     */
    public GspMetadata getMetadataWithRuntime(String metadataId) {
        return MetadataUtilityWithRuntime.getMetadata(metadataId);
    }

    /**
     * rtc-viewmodel存在调用
     * 不允许更改对应方法名称
     *
     * @param metadataId
     * @return
     */
    public GspMetadata getMetadataInRuntime(String metadataId) {
        return this.getMetadataWithRuntime(metadataId);
    }


    /**
     * 根据元数据相对路径获取工程信息
     *
     * @param metadataRelativePath
     * @return
     */
    public Optional<MetadataProject> getMetadataProject(String metadataRelativePath) {
        return MetadataProjectUtility.getMetadataProject(metadataRelativePath);
    }

    /**
     * 根据元数据相对路径获取工程路径
     *
     * @param metadataRelativePath
     * @return
     */
    public String getMetadataProjectPath(String metadataRelativePath) {
        Optional<MetadataProject> projectInfo = getMetadataProject(metadataRelativePath);

        return projectInfo.map(MetadataProject::getProjectPath).orElse(null);
    }

    /**
     * 获取开发元数据绝对路径
     *
     * @param relativePath
     * @return
     */
    public String getAbsolutePath(String relativePath) {
        //开发根路径
        String devRootPath = ManagerUtils.getDevRootPath();
        if (relativePath.startsWith(devRootPath)) {
            return relativePath;
        }

        return FileUtility.combine(devRootPath, relativePath);
    }


    // TODO：迁移到WebCommmand项目中
    public HashMap<String, GspMetadata> getReferenceComponentMetadata(GspMetadata commandMetadata, AfterGetReferenceWebComponentMetadataInvoker afterGetReferenceWebComponentMetadata, Object... parameterArray) {
        if (commandMetadata == null) {
            return null;
        }

        HashMap<String, GspMetadata> componentMetadataCollection = new HashMap<>();
        getReferenceWebComponentMetadata(commandMetadata, componentMetadataCollection, null, null, afterGetReferenceWebComponentMetadata, parameterArray);
        return componentMetadataCollection;
    }

    private void getReferenceWebComponentMetadata(GspMetadata commandMetadata, HashMap<String, GspMetadata> cmpList, List<BranchCommandItem> items,
                                                  HashMap<String, GspMetadata> componentCacheList, AfterGetReferenceWebComponentMetadataInvoker afterGetReferenceWebComponentMetadata, Object... parameterArray) {
        WebCommandsMetadata commandMetadataContent = null;
        try {
            commandMetadataContent = (WebCommandsMetadata) commandMetadata.getContent();
        } catch (RuntimeException e) {
            throw new WebCustomException(WebCommonExceptionConstant.WEB_COMMON_ERROR_0006, new String[]{commandMetadata.getHeader().getId()} , e);
        }

        if (commandMetadataContent == null) {
            return;
        }

        if (items == null || items.isEmpty()) {
            if (commandMetadataContent.getOperations() != null && commandMetadataContent.getOperations().size() > 0) {
                for (WebCommand webCommandItem : commandMetadataContent.getOperations()) {
                    getReferenceWebComponentCommandItemList(commandMetadata, webCommandItem.getItems(), cmpList, componentCacheList, afterGetReferenceWebComponentMetadata, parameterArray);
                }
            }
        } else {
            for (BranchCommandItem branchCommandItem : items) {
                getReferenceWebComponentCommandItemList(commandMetadata, branchCommandItem.getItems(), cmpList, componentCacheList, afterGetReferenceWebComponentMetadata, parameterArray);
            }
        }
    }


    private void getReferenceWebComponentCommandItemList(GspMetadata commandMetadata, List<CommandItem> commandItemList, HashMap<String, GspMetadata> componentMetadataCollection,
                                                         HashMap<String, GspMetadata> componentCacheList, AfterGetReferenceWebComponentMetadataInvoker afterGetReferenceWebComponentMetadata, Object... parameterArray) {
        if (commandItemList == null || commandItemList.size() == 0) {
            return;
        }

        for (CommandItem item : commandItemList) {
            switch (item.getItemType()) {
                case MethodRefer:
                    CmpMethodRefering methodReferingItem = (CmpMethodRefering) item;
                    MetadataGetterParameter metadataGetterParameter = MetadataGetterParameter.getNewInstance(methodReferingItem.getComponentId(), commandMetadata.getRelativePath(), MetadataTypeEnum.Component);
                    metadataGetterParameter.setSourceMetadata(commandMetadata, MetadataTypeEnum.Command);

                    GspMetadata webComponentMetadata = getMetadataWithDesign(metadataGetterParameter);

                    if (!componentMetadataCollection.containsKey(methodReferingItem.getComponentId())) {
                        if (componentCacheList != null && componentCacheList.containsKey(methodReferingItem.getComponentId())) {
                            GspMetadata cmpMetadata = componentCacheList.get(methodReferingItem.getComponentId());
                            componentMetadataCollection.put(cmpMetadata.getHeader().getId(), cmpMetadata);
                        } else {
                            WebComponentMetadata cmpMetadataContent = (WebComponentMetadata) webComponentMetadata.getContent();
                            if (cmpMetadataContent == null) {
                                throw new WebCustomException(WebCommonExceptionConstant.WEB_COMMON_ERROR_0007, new String[]{methodReferingItem.getComponentId()});
                            }


                            componentMetadataCollection.put(webComponentMetadata.getHeader().getId(), webComponentMetadata);
                            if (componentCacheList != null) {
                                componentCacheList.put(webComponentMetadata.getHeader().getId(), webComponentMetadata);
                            }
                        }
                    }

                    afterGetReferenceWebComponentMetadata.invoke(commandMetadata, methodReferingItem, webComponentMetadata, parameterArray);
                    break;
                case BranchCollection:
                    getReferenceWebComponentMetadata(null, componentMetadataCollection, ((BranchCollectionCommandItem) item).getItems(), componentCacheList, afterGetReferenceWebComponentMetadata, parameterArray);
                    break;
                default:
                    break;
            }
        }
    }

    /**
     * 获取开发期的根路径
     *
     * @return
     */
    public String getDevRootPath() {
        return FileUtility.getPlatformIndependentPath(ManagerUtils.getDevRootPath());
    }

    /**
     * 获取开发时工作空间绝对路径
     *
     * @return
     */
    public String getDevRootAbsolutePath() {
        String devRootPath = getDevRootPath();
        if (!FileUtility.isAbsolute(devRootPath)) {
            devRootPath = FileUtility.getAbsolutePathHead(devRootPath) + devRootPath;
        }
        return devRootPath;
    }
}
